//
//  CTCollectionViewSectionInfo.m
//
//  Created by Andney Jack on 2021/6/2.
//

#import "CTCollectionViewSectionInfo.h"

@interface CTCollectionViewSectionInfo ()
{
    BOOL _didRecalculate;
    CGFloat _maxColumnHeight;
}
@property (nonatomic, strong) NSMutableArray<NSNumber *> *columnHeights;
@end

@implementation CTCollectionViewSectionInfo

- (instancetype)init {
    self = [super init];
    if (self) {
        _headerInsets = UIEdgeInsetsZero;
        _footerInsets = UIEdgeInsetsZero;
        _sectionInsets = UIEdgeInsetsZero;
        _safeAreaInsets = UIEdgeInsetsZero;

        _itemRenderDirection = CTCollectionViewItemRenderDirectionShortestFirst;
        _headerBounds = CTItemBoundsMake(0, 0);
        _footerBounds = CTItemBoundsMake(0, 0);
    }
    return self;
}

#pragma mark - Public

- (void)setNeedRecalculate {
    _didRecalculate = NO;
}

- (void)recalculateIfNeeded {
    if (!_didRecalculate) {
        [self m_recalculate];
        _didRecalculate = YES;
    }
}

- (BOOL)hasFloatingHeaderOrFooter {
    return (self.headerPinToVisible || self.footerPinToVisible);
}

#pragma mark -

- (CGFloat)totalSectionHeight {
    return [self sectionContentHeight] + [self headerTotalHeight] + [self footerTotalHeight];
}

- (CGFloat)sectionContentHeight {
    [self recalculateIfNeeded];
    return _maxColumnHeight;
}

- (CGFloat)headerTotalHeight {
    return self.headerInsets.top + self.headerHeight + self.headerInsets.bottom;
}

- (CGFloat)footerTotalHeight {
    return self.footerInsets.top + self.footerHeight + self.footerInsets.bottom;
}

- (CGFloat)sectionBottom {
    return self.top + [self totalSectionHeight];
}

- (void)setColumnCount:(NSInteger)columnCount {
    _columnCount = columnCount;

    if (_columnHeights) {
        [_columnHeights removeAllObjects];

        for (NSInteger i = 0; i < columnCount; i++) {
            [_columnHeights addObject:@(0.0f)];
        }
    }
}

- (NSMutableArray<NSNumber *> *)columnHeights {
    if (!_columnHeights) {
        _columnHeights = [[NSMutableArray alloc] initWithCapacity:0];

        for (NSInteger i = 0; i < _columnCount; i++) {
            [_columnHeights addObject:@(0.0f)];
        }
    }
    return _columnHeights;
}

- (NSMutableArray<CTCollectionViewItemInfo *> *)allItemInfos {
    if (!_allItemInfos) {
        _allItemInfos = [[NSMutableArray alloc] initWithCapacity:0];
    }
    return _allItemInfos;
}

- (void)m_recalculate {
    if (_itemRenderDirection == CTCollectionViewItemRenderDirectionColumnFirst) {
        [self m_itemLayoutByColumnFirst];
    } else {
        [self m_itemLayoutByWaterfall];
    }

    // header
    if (self.headerHeight > 0) {
        self.headerAttribute = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:CTCollectionElementKindSectionHeader
                                                                                              withIndexPath:[NSIndexPath indexPathForItem:0 inSection:self.section]];
        CGFloat headerWidth = self.collectionViewWidth - (self.safeAreaInsets.left + self.safeAreaInsets.right) - (self.headerInsets.left + self.headerInsets.right);
        if (headerWidth < 0.0) {
            headerWidth = 0.0;
        }
        CGRect headerFrame = CGRectMake(self.safeAreaInsets.left + self.headerInsets.left,
                                        self.top + self.headerInsets.top,
                                        headerWidth,
                                        self.headerHeight);
        self.headerAttribute.frame = headerFrame;
        self.headerBounds = CTItemBoundsMake(CGRectGetMinY(headerFrame), CGRectGetMaxY(headerFrame));
    } else {
        self.headerAttribute = nil;
    }

    // footer
    if (self.footerHeight > 0) {
        self.footerAttribute = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:CTCollectionElementKindSectionFooter
                                                                                              withIndexPath:[NSIndexPath indexPathForItem:0 inSection:self.section]];
        CGFloat footerWidth = self.collectionViewWidth - (self.safeAreaInsets.left + self.safeAreaInsets.right) - (self.footerInsets.left + self.footerInsets.right);
        if (footerWidth < 0.0) {
            footerWidth = 0.0;
        }

        CGRect footerFrame = CGRectMake(self.safeAreaInsets.left + self.footerInsets.left,
                                        self.top + [self headerTotalHeight] + _maxColumnHeight,
                                        footerWidth,
                                        self.footerHeight);
        self.footerAttribute.frame = footerFrame;
        self.footerBounds = CTItemBoundsMake(CGRectGetMinY(footerFrame), CGRectGetMaxY(footerFrame));
    } else {
        self.footerAttribute = nil;
    }
}

#pragma mark - Item Layout

- (void)m_itemLayoutByWaterfall {
    CGFloat baseTop = self.top + self.headerInsets.top + self.headerHeight + self.headerInsets.bottom + self.sectionInsets.top;
    CGFloat baseOffsetX = self.safeAreaInsets.left + self.sectionInsets.left;

    for (NSInteger item = 0; item < self.allItemInfos.count; item++) {
        CTCollectionViewItemInfo *itemInfo = [self.allItemInfos objectAtIndex:item];

        NSInteger columnIndex = [self nextColumnIndexForItem:item inSection:self.section];
        CGFloat offsetY = self.columnHeights[columnIndex].floatValue;

        itemInfo.columnIndex = columnIndex;
        itemInfo.itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:itemInfo.item inSection:itemInfo.section]];

        CGFloat offsetX = baseOffsetX;
        if (columnIndex > 0) {
            for (NSInteger i = 0; i < columnIndex; i++) {
                offsetX += self.columnWidth + self.columnSpacing;
            }
        }
        itemInfo.itemAttributes.frame = CGRectMake(offsetX,
                                                   baseTop + offsetY,
                                                   self.columnWidth,
                                                   itemInfo.itemHeight);

        self.columnHeights[columnIndex] = [NSNumber numberWithFloat:offsetY + itemInfo.itemHeight + self.interitemSpacing];
    }

    CGFloat maxHeight = 0;
    for (NSNumber *num in self.columnHeights) {
        CGFloat height = num.floatValue;
        if (height > maxHeight) {
            maxHeight = height;
        }
    }

    if (maxHeight > 0) {
        maxHeight -= self.interitemSpacing;
    }
    _maxColumnHeight = self.sectionInsets.top + maxHeight + self.sectionInsets.bottom;
}

- (void)m_itemLayoutByColumnFirst {
    CGFloat itemTotalHeight = 0.0f;
    for (CTCollectionViewItemInfo *item in self.allItemInfos) {
        itemTotalHeight += item.itemHeight + self.interitemSpacing;
    }
    if (self.allItemInfos.count > 0) {
        itemTotalHeight -= self.interitemSpacing;
    }

    CGFloat averageHeight = itemTotalHeight / self.columnCount; // average height for one column
    CGFloat baseTop = self.top + self.headerInsets.top + self.headerHeight + self.headerInsets.bottom + self.sectionInsets.top;

    CGFloat offsetX = self.safeAreaInsets.left + self.sectionInsets.left;
    CGFloat offsetY = 0;
    NSInteger columnIndex = 0;

    CGFloat maxHeight = 0.0;
    for (NSInteger idx = 0; idx < self.allItemInfos.count; idx++) {
        CTCollectionViewItemInfo *itemInfo = [self.allItemInfos objectAtIndex:idx];
        BOOL shouldAddColumnIndex = NO;

        if (offsetY > 0) {
            if (self.allItemInfos.count <= self.columnCount) {
                shouldAddColumnIndex = YES;
            } else if (columnIndex < self.columnCount - 1 && offsetY + itemInfo.itemHeight * 0.5 > averageHeight) {
                shouldAddColumnIndex = YES;
            }
        }

        if (shouldAddColumnIndex) {
            CGFloat lastColumnWidth = self.columnWidth;
            columnIndex++;

            CGFloat previousColumnHeight = offsetY - self.interitemSpacing;
            if (maxHeight < previousColumnHeight) {
                maxHeight = previousColumnHeight;
            }

            offsetX += lastColumnWidth + self.columnSpacing;
            offsetY = 0;
        }

        itemInfo.columnIndex = columnIndex;
        itemInfo.itemWidth = self.columnWidth;
        itemInfo.itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:itemInfo.item inSection:itemInfo.section]];
        itemInfo.itemAttributes.frame = CGRectMake(offsetX, offsetY + baseTop, self.columnWidth, itemInfo.itemHeight);
        offsetY += itemInfo.itemHeight + self.interitemSpacing;
    }

    CGFloat lastColumnHeight = offsetY > 0 ? offsetY - self.interitemSpacing : 0;
    if (maxHeight < lastColumnHeight) {
        maxHeight = lastColumnHeight;
    }

    _maxColumnHeight = self.sectionInsets.top + maxHeight + self.sectionInsets.bottom;
}

- (NSUInteger)nextColumnIndexForItem:(NSInteger)item inSection:(NSInteger)section {
    switch (_itemRenderDirection) {
        case CTCollectionViewItemRenderDirectionLeftToRight: {
            return item % self.columnCount;
        }
        break;
        case CTCollectionViewItemRenderDirectionRightToLeft: {
            return (self.columnCount - 1) - (item % self.columnCount);
        }
        break;

        default: {
            // ShortestFirst
            NSInteger columnIndex = 0;
            if (self.columnHeights.count > 0) {
                CGFloat minHeight = self.columnHeights[0].floatValue;
                for (NSInteger idx = 1; idx < self.columnHeights.count; idx++) {
                    CGFloat height = self.columnHeights[idx].floatValue;
                    if (height < minHeight) {
                        minHeight = height;
                        columnIndex = idx;
                    }
                }
            }

            return columnIndex;
        }

        break;
    }
}

@end

#pragma mark - ————————— CTCollectionViewItemInfo —————————

@implementation CTCollectionViewItemInfo

@end
